package cn.edu.scau.cmi.ema.service;

import cn.edu.scau.cmi.ema.base.CmiPagination;
import cn.edu.scau.cmi.ema.domain.Detect;
import cn.edu.scau.cmi.ema.domain.Event;
import cn.edu.scau.cmi.ema.domain.Subject;
import cn.edu.scau.cmi.ema.service.base.CmiEmaService;
import cn.edu.scau.cmi.ema.util.CmiSqlUtil;
import org.hibernate.Session;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Projections;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.Query;
import java.util.*;
@Service("SubjectService")
@Transactional

public class SubjectServiceImpl extends CmiEmaService implements SubjectService {
	public SubjectServiceImpl() {
	}

	@Transactional
	public Subject saveSubject(Subject subject) {
		Set<Detect> existingDetectsForSubject = new HashSet<Detect>();
		Set<Event> existingEventsForSubject = new HashSet<Event>();

		Set<Detect> selectedDetectsForSubject = subject.getDetectsForSubject();
		Set<Event> selectedEventsForSubject = subject.getEventsForSubject();

		if (subject.getId() == null) {

			Subject existingSubject = subjectDAO.findSubjectByPhysicalPrimaryKey(subject.getName());

			if (isEquals(subject, existingSubject)) {
				return existingSubject;
			}

			subject = subjectDAO.store(subject);
			subjectDAO.flush();

			for (Detect relatedDetect : selectedDetectsForSubject) {
				relatedDetect.setSubject(subject);
				detectDAO.store(relatedDetect);
			}
			for (Event relatedEvent : selectedEventsForSubject) {
				relatedEvent.setSubject(subject);
				eventDAO.store(relatedEvent);
			}
		} else {
			Subject existingSubject = subjectDAO.findSubjectByPrimaryKey(subject.getId());

			existingDetectsForSubject = existingSubject.getDetectsForSubject();
			existingEventsForSubject = existingSubject.getEventsForSubject();

			Set<Detect> prepareDeleteDetectsForSubject = new HashSet<Detect>();
			Set<Detect> prepareAddDetectsForSubject = new HashSet<Detect>();

			prepareDeleteDetectsForSubject.addAll(selectedDetectsForSubject);
			prepareDeleteDetectsForSubject.addAll(existingDetectsForSubject);

			prepareAddDetectsForSubject.addAll(selectedDetectsForSubject);
			prepareAddDetectsForSubject.addAll(existingDetectsForSubject);

			prepareDeleteDetectsForSubject.removeAll(selectedDetectsForSubject);
			prepareAddDetectsForSubject.removeAll(existingDetectsForSubject);

			for (Detect relatedDetect : prepareAddDetectsForSubject) {
				relatedDetect.setSubject(subject);
				detectDAO.store(relatedDetect);
			}

			for (Detect relatedDetect : prepareDeleteDetectsForSubject) {
				relatedDetect.setSubject(null);
				detectDAO.store(relatedDetect);
			}
			Set<Event> prepareDeleteEventsForSubject = new HashSet<Event>();
			Set<Event> prepareAddEventsForSubject = new HashSet<Event>();

			prepareDeleteEventsForSubject.addAll(selectedEventsForSubject);
			prepareDeleteEventsForSubject.addAll(existingEventsForSubject);

			prepareAddEventsForSubject.addAll(selectedEventsForSubject);
			prepareAddEventsForSubject.addAll(existingEventsForSubject);

			prepareDeleteEventsForSubject.removeAll(selectedEventsForSubject);
			prepareAddEventsForSubject.removeAll(existingEventsForSubject);

			for (Event relatedEvent : prepareAddEventsForSubject) {
				relatedEvent.setSubject(subject);
				eventDAO.store(relatedEvent);
			}

			for (Event relatedEvent : prepareDeleteEventsForSubject) {
				relatedEvent.setSubject(null);
				eventDAO.store(relatedEvent);
			}
			subject = subjectDAO.store(subject);
			subjectDAO.flush();
		}

		detectDAO.flush();
		eventDAO.flush();
		return subject;
	}

	public void deleteSubject(Subject subject) {
		subjectDAO.remove(subject);
		subjectDAO.flush();
	}

	@Transactional
	public void deleteSubjects(Set<Integer> deleteSubjectsId) {
		Query query = subjectDAO.createNamedQuery("deleteSubjectsById", -1, -1);
		query.setParameter(1, deleteSubjectsId);
		query.executeUpdate();
	}

	@Transactional
	public Set<Subject> loadSubjects() {
		return subjectDAO.findAllSubjects();

	}

	@Transactional
	public Set<Subject> loadReferenceSubjects(Set<Subject> subjects) {
		Iterator<Subject> subjectsIterator = subjects.iterator();
		Set<Subject> referencedSubjects = new HashSet<>();
		while (subjectsIterator.hasNext()) {
			Subject currentSubject = subjectsIterator.next();

			if (!currentSubject.getDetectsForSubject().isEmpty()) {
				referencedSubjects.add(currentSubject);
				continue;
			}

			if (!currentSubject.getEventsForSubject().isEmpty()) {
				referencedSubjects.add(currentSubject);
				continue;
			}
		}
		return referencedSubjects;
	}

	public Set<Subject> loadSubjectsByIdSet(Set<Integer> subjectIdSet) {

		Iterator<Integer> subjectIdIterator = subjectIdSet.iterator();
		Set<Subject> subjectSet = new HashSet<>();
		while (subjectIdIterator.hasNext()) {
			Integer id = subjectIdIterator.next();
			Subject currentSubject = subjectDAO.findSubjectById(id);
			subjectSet.add(currentSubject);
		}
		return subjectSet;
	}

	@Transactional
	public List<Subject> findAllSubjects(Integer startResult, Integer maxRows) {
		return new java.util.ArrayList<Subject>(subjectDAO.findAllSubjects(startResult, maxRows));
	}

	@Transactional
	public Subject findSubjectByPrimaryKey(Integer id) {
		return subjectDAO.findSubjectByPrimaryKey(id);
	}

	@Transactional
	public Integer countSubjects() {
		return ((Long) subjectDAO.createQuerySingleResult("select count(o) from Subject o").getSingleResult())
				.intValue();
	}

	@Transactional
	public Integer countSubjects(CmiPagination page) {
		String sql = CmiSqlUtil.createCountHql(page, Subject.class);
		return ((Long) subjectDAO.createQuerySingleResult(sql).getSingleResult()).intValue();
	}

	/**
	 * search for name with ajax
	 *
	 */
	@Transactional
	public Integer countSubjectsByName(String queryName) {
		Query query = subjectDAO.createNamedQuery("countSubjectsByName", -1, -1);
		query.setParameter(1, queryName);
		return new Long((long) query.getSingleResult()).intValue();
	}

	@Transactional
	public Integer countSubjectsByDetachedCriteria(CmiPagination page) {
		DetachedCriteria detachedCriteria = CmiSqlUtil.createDetachedCriteria(page, Subject.class);
		Session session = (Session) entityManager.getDelegate();
		detachedCriteria.setProjection(Projections.rowCount());
		return new Long((long) detachedCriteria.getExecutableCriteria(session).uniqueResult()).intValue();
	}

	public Map<String, ?> loadSubjectObjectReferencedSet(Subject subject) {
		HashMap<String, Object> objectReferencedSetMap = new HashMap<>();

		Set<Integer> detectsIds = new HashSet<Integer>();
		for (Detect obj : subject.getDetectsForSubject()) {

			detectsIds.add(obj.getId());
		}
		objectReferencedSetMap.put("detectsIds", detectsIds);
		Set<Integer> eventsIds = new HashSet<Integer>();
		for (Event obj : subject.getEventsForSubject()) {

			eventsIds.add(obj.getId());
		}
		objectReferencedSetMap.put("eventsIds", eventsIds);

		return objectReferencedSetMap;
	}

	public Map<String, ?> loadSubjectClassReferencedSet(Subject subject) {
		HashMap<String, Object> classReferencedSetMap = new HashMap<>();
		classReferencedSetMap.put("detects", detectDAO.findAllDetects());
		classReferencedSetMap.put("events", eventDAO.findAllEvents());

		return classReferencedSetMap;
	}

	public boolean isEquals(Subject subject, Object obj) {
		if (obj == subject)
			return true;
		if (!(obj instanceof Subject))
			return false;
		Subject comparedSubject = (Subject) obj;
		if (!CheckPropertyEqual(subject.getName(), comparedSubject.getName())) {
			return false;
		}
		return true;
	}

	public static <T> boolean CheckPropertyEqual(T originProperty, T comparedProperty) {
		// 基本类型经过参数传递后会自动装箱
		String[] className = originProperty.getClass().getName().split("\\.");
		if (className[0].equals("java") && className[1].equals("math")) {
			return ((Comparable) originProperty).compareTo(comparedProperty) == 0 ? true : false;
		} else {
			return originProperty.equals(comparedProperty);
		}
	}
}

